package com.ly.mssp.license.client.registry;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ly.mssp.license.client.registry.meta.ImageHistory;
import com.ly.mssp.license.client.registry.meta.ManifestChild;
import com.ly.mssp.license.client.registry.meta.ManifestConfig;
import com.ly.mssp.license.client.registry.meta.ManifestMeta;

public class ImageClient {
	
	private String tagName;
	
	private String imageName;
	
	private String firstLevel;
	
	private String architecture;
	
	private MultipartFile file;
	
	private List<String> layers=new ArrayList();
	
	private List<String> digests=new ArrayList();
	
	private HashMap<String, byte[]> data=new HashMap();
	
	private HashMap<String, JSONObject> histories=new HashMap();
	
	private String config;
	
	private String id;
	
	private int size;

	public ImageClient(MultipartFile file) {
		this.file=file;
	}
	
	public String getTagName() {
		return tagName;
	}

	public String getImageName() {
		return imageName;
	}
	
	public List<String> getDigests() {
		return this.digests;
	}
	
	public String getArchitecture() {
		return this.architecture;
	}
	
	public String getConfig() {
		return this.config;
	}

	public String getId(String digest) {
		for (int i=0;i< digests.size();i++) {
			String item=digests.get(i);
			if (item.equals(digest) && i<layers.size()) {
				return layers.get(i);
			}
		}
		return null;
	}
	
	public byte[] getContent(String id) {
		return data.get(id);
	}
	
	public JSONObject getHistory(String id) {
		return histories.get(id);
	}

	public void parse() {
		try {
			InputStream in=file.getInputStream();
			TarArchiveInputStream tar=new TarArchiveInputStream(in);
			try {
				TarArchiveEntry entry=null;
				while ((entry=tar.getNextTarEntry())!=null) {
					String name=entry.getName();
					if ("manifest.json".equals(name)) {
						byte[] content=getContent(tar);
						if (content!=null) {
							parseManifest(new String(content));
						}
					}else if ("repositories".equals(name)) {
						byte[] content=getContent(tar);
						if (content!=null) {
							parseRepositories(new String(content));
						}
					}else if (name.endsWith(".json")) {
						byte[] content=getContent(tar);
						if (content!=null) {
							parseConfig(name, new String(content));
						}
					}else if (name.endsWith(".tar")){
						byte[] content=getContent(tar);
						if (content!=null) {
							content=GzipUtils.zip(content);
							parseLayer(name, content);
						}
					}else if (name.endsWith("/json")){
						byte[] content=getContent(tar);
						if (content!=null) {
							parseHistory(new String(content));
						}
					}
				}
				
				this.calcSum();
				
			}finally {
				tar.close();
				in.close();
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	private void calcSum() {
		
		JSONObject config=JSONObject.parseObject(this.config);
		JSONObject rootfs=config.getJSONObject("rootfs");
		if (rootfs!=null) {
			rootfs.remove("diff_ids");
		}
		
		JSONArray arr=new JSONArray();
		
		for (int i=0;i<digests.size();i++) {
			String digest=digests.get(i);
			
			String id=this.getId(digest);
			
			byte[] content=this.getContent(id);
			
			digest=GzipUtils.blobSum(content);
			
			digests.set(i, digest);
			
			arr.add(digest);
		}
		
		if (rootfs!=null) {
			rootfs.put("diff_ids", arr);
			this.config=JSONObject.toJSONString(config);
		}		
	}
	
	private void parseManifest(String manifest) {
		try {
			JSONArray obj=JSONObject.parseArray(manifest);
			JSONObject _manifest=obj.getJSONObject(0);
			JSONArray arr=_manifest.getJSONArray("Layers");
			for (int i=0;i<arr.size();i++) {
				String val=arr.getString(i);
				String[] items=val.split("/");
				this.layers.add(items[0]);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	private void parseRepositories(String repositories) {
		try {
			JSONObject obj=JSONObject.parseObject(repositories);
			if (obj.size()>0) {
				String val=obj.keySet().iterator().next();
				JSONObject child=obj.getJSONObject(val);
				if (child!=null && child.size()>0) {
					this.tagName=child.keySet().iterator().next();
					
					this.firstLevel=child.getString(this.tagName);
				}
				
				String[] items=val.split("/");
				for (int i=1;i<items.length;i++) {
					if (i==1) {
						this.imageName=items[1];
					}else {
						this.imageName=this.imageName+"/"+items[i];
					}
				}
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	private void parseConfig(String name, String config) {
		try {
			
			this.config=config;
			
			JSONObject obj=JSONObject.parseObject(config);
			
			this.architecture=obj.getString("architecture");		
						
			this.id=name.substring(0, name.length()-5);
			
			byte[] buf=config.getBytes();
			this.size=buf.length;
			this.id=GzipUtils.blobSum(buf);
			
			JSONObject rootfs=obj.getJSONObject("rootfs");
			if (rootfs!=null) {
				JSONArray diff_ids=rootfs.getJSONArray("diff_ids");
				if (diff_ids!=null) {
					for (int i=0;i<diff_ids.size()-1;i++) {
						String digest=diff_ids.getString(i);
						this.digests.add(digest);
					}
				}
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	private void parseLayer(String name, byte[] content) {
		String[] items=name.split("/");
		data.put(items[0], content);
	}
	
	private void parseHistory(String content) {
		try {
			JSONObject obj=JSONObject.parseObject(content);
			String id=obj.getString("id");
			
			histories.put(id, obj);
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 
	 * 读取压缩文件内容
	 * 
	 * @param stream
	 * @return
	 */
	private byte[] getContent(ArchiveInputStream  stream) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
        	try {
	            byte[] buffer = new byte[1024];
	            int len=-1;
	            while ((len=stream.read(buffer))!=-1) {
	                baos.write(buffer, 0, len);
	            }
	            return baos.toByteArray();
        	}finally {
                baos.close();
        	}
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
	}
	
	public ManifestMeta getManifest() {
		
		ManifestMeta meta=new ManifestMeta();
		
		byte[] content=this.config.getBytes();
		String digest=GzipUtils.blobSum(content);
		
		ManifestConfig config=new ManifestConfig();
		meta.setConfig(config);
		config.setDigest(digest);
		config.setSize(content.length);
		
		meta.setName(this.imageName);
		meta.setTag(this.tagName);
		meta.setArchitecture(this.architecture);
		
		for (int i=digests.size()-1;i>=0;i--) {
			
			digest=digests.get(i);
			
			String id=this.getId(digest);
			
			byte[] buf=this.getContent(id);
			
			//buf=GzipUtils.zip(buf);
			
			//digest=GzipUtils.blobSum(buf);
			
			int size=buf.length;

			ManifestChild layer=new ManifestChild();
			
			layer.setDigest(digest);
			
			layer.setSize(size);
					
			meta.addLayer(layer);
			
//			JSONObject _history=histories.get(id);
//			if (_history!=null) {
//				ImageHistory history=convert(_history);
//								
//				meta.addHistory(history);
//			}
		}		
		
		return meta;
	}

	private ImageHistory convert(JSONObject json) {
		ImageHistory history=new ImageHistory();
		history.setV1Compatibility(json.toJSONString());
		return history;
	}


	public void close() {
		// TODO Auto-generated method stub
		
	}
}
